package com.cloudfun.campusshare.configuration.jpa;

import com.cloudfun.campusshare.common.response.PageResult;
import com.cloudfun.campusshare.configuration.jpa.specification.PropertyFilterSpecification;
import com.cloudfun.campusshare.configuration.jpa.specification.PropertySpecification;
import com.cloudfun.campusshare.configuration.jpa.specification.SpecificationUtil;
import com.cloudfun.campusshare.configuration.jpa.util.PropertyFilter;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author wangxiyuan
 * @description
 * @createDate 2021/5/19 18:07
 **/
@Slf4j
public class GaodaoRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
        implements GaodaoRepository<T, ID>{
    private final EntityManager entityManager;

    public GaodaoRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
    }

    @Override
    public T findOneByProperty(String property, String value) {
        Optional one = super.findOne(SpecificationUtil.get(property,value));
        if(!one.isPresent()){
            return null;
        }
        return (T) one.get();
    }

    @Override
    public List<T> findListByProperty(String property, String value) {
        return super.findAll(SpecificationUtil.get(property,value));
    }

    @Override
    public T findOneByFilters(List<PropertyFilter> propertyFilters) {
        Optional one = super.findOne(SpecificationUtil.get(propertyFilters));
        if(!one.isPresent()){
            return null;
        }
        return (T) one.get();
    }

    @Override
    public List<T> findListByFilters(List<PropertyFilter> propertyFilters) {
        return super.findAll(SpecificationUtil.get(propertyFilters));
    }


    @Override
    public Long findCount(String sql) {
        return count(sql);
    }

    @Override
    public <X> X findSingleQuery(String sqlString, Class<X> clazz) {
        return findSingleQuery(sqlString,null,clazz);
    }

    @Override
    public <X> X findSingleQuery(String sql, Object[] params, Class<X> clazz) {
        check(sql, BeanUtils.isSimpleProperty(clazz));
        Query query = getQuery(sql, params,null);
        log.debug("执行sql为{}",sql);
        // 这种方案要求DTO属性名称与查询字段保持一致
        return (X) query.unwrap(NativeQueryImpl.class)
                .setResultTransformer(MyTransformers.aliasToBean(clazz)).getSingleResult();
    }

    @Override
    public <X> List<X> findNativeNamedQuery(String sql, Object[] params, Class<X> clazz) {
        check(sql, BeanUtils.isSimpleProperty(clazz));
        Query query = getQuery(sql, params,null);
        log.debug("执行sql为{}",sql);
        // 这种方案要求DTO属性名称与查询字段保持一致
        return query.unwrap(NativeQueryImpl.class)
                .setResultTransformer(MyTransformers.aliasToBean(clazz)).getResultList();
    }

    @Override
    public <X> PageResult<X> findNativeNamedQuery(String sqlString, PageRequest page, Class<X> clazz) {
        return findNativeNamedQuery(sqlString,null,page,clazz);
    }

    @Override
    public <X> PageResult<X> findNativeNamedQuery(String sql, Object[] params, PageRequest page, Class<X> clazz) {
        // 获取总的记录数
        Long totalCount = findCount(sql);
        PageResult<X> pageResult = new PageResult<>();
        if(totalCount != 0){
            Query query = getQuery(sql, params, page);
            List resultList = query.unwrap(NativeQueryImpl.class)
                    .setResultTransformer(MyTransformers.aliasToBean(clazz)).getResultList();
            pageResult.setTotalSize(totalCount);
            pageResult.setElements(resultList);
        }else{
            pageResult.setElements(new ArrayList<>());
        }
        return pageResult;
    }

    /**
     * 统计sql查询到的数量
     * @param sql
     * @return
     */
    private Long count(String sql){
        StringBuilder sb = new StringBuilder();
        sb.append("select count(*) from (").append(sql).append(") a");
        return ((BigInteger) entityManager.createNativeQuery(sb.toString()).getSingleResult()).longValue();
    }

    private Query getQuery(String sql, Object[] params,PageRequest page) {
        Query query = entityManager.createNativeQuery(sql);
        if(page != null){
            query.setFirstResult((page.getPageNumber() - 1 )* page.getPageSize()).setMaxResults(page.getPageSize());
        }
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }
        return query;
    }

    private void check(String sql, boolean simpleProperty) {
        Assert.hasText(sql, "查询sql为空");
        if (simpleProperty) {
            throw new RuntimeException("clazz不应为基本类型");
        }
    }
}
